{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# KernelSHAP: combining preprocessor and predictor"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-info\">\n",
    "Note\n",
    "\n",
    "To enable SHAP support, you may need to run\n",
    "    \n",
    "```bash\n",
    "pip install alibi[shap]\n",
    "```\n",
    "\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Introduction"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In [this](kernel_shap_adult_lr.ipynb) example, we showed that the categorical variables can be handled by fitting the explainer on preprocessed data and passing preprocessed data to the `explain` call. To handle the categorical variables, we either group them explicitly or sum the estimated shap values for each encoded shap dimension. An alternative way is to define our black-box model to include the preprocessor, as shown in [this](anchor_tabular_adult.ipynb) example. We now show that these methods give the same results."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import shap\n",
    "shap.initjs()\n",
    "\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "\n",
    "from alibi.explainers import KernelShap\n",
    "from alibi.datasets import fetch_adult\n",
    "from scipy.special import logit\n",
    "from sklearn.compose import ColumnTransformer\n",
    "from sklearn.impute import SimpleImputer\n",
    "from sklearn.linear_model import LogisticRegression\n",
    "from sklearn.metrics import accuracy_score, confusion_matrix, ConfusionMatrixDisplay\n",
    "from sklearn.model_selection import cross_val_score, train_test_split\n",
    "from sklearn.pipeline import Pipeline\n",
    "from sklearn.preprocessing import StandardScaler, OneHotEncoder"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Data preparation"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Load and split"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The `fetch_adult` function returns a `Bunch` object containing the features, the targets, the feature names and a mapping of categorical variables to numbers."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "dict_keys(['data', 'target', 'feature_names', 'target_names', 'category_map'])"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "adult = fetch_adult()\n",
    "adult.keys()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "data = adult.data\n",
    "target = adult.target\n",
    "target_names = adult.target_names\n",
    "feature_names = adult.feature_names\n",
    "category_map = adult.category_map"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that for your own datasets you can use our utility function `gen_category_map` to create the category map."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "from alibi.utils import gen_category_map"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "np.random.seed(0)\n",
    "data_perm = np.random.permutation(np.c_[data, target])\n",
    "data = data_perm[:,:-1]\n",
    "target = data_perm[:,-1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "idx = 30000\n",
    "X_train,y_train = data[:idx,:], target[:idx]\n",
    "X_test, y_test = data[idx+1:,:], target[idx+1:]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Create feature transformation pipeline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Create feature pre-processor. Needs to have 'fit' and 'transform' methods. Different types of pre-processing can be applied to all or part of the features. In the example below we will standardize ordinal features and apply one-hot-encoding to categorical features.\n",
    "\n",
    "Ordinal features:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "ordinal_features = [x for x in range(len(feature_names)) if x not in list(category_map.keys())]\n",
    "ordinal_transformer = Pipeline(steps=[('imputer', SimpleImputer(strategy='median')),\n",
    "                                      ('scaler', StandardScaler())])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Categorical features:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "categorical_features = list(category_map.keys())\n",
    "categorical_transformer = Pipeline(steps=[('imputer', SimpleImputer(strategy='median')),\n",
    "                                          ('onehot', OneHotEncoder(drop='first', handle_unknown='error'))])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that in order to be able to interpret the coefficients corresponding to the categorical features, the option `drop='first'` has been passed to the `OneHotEncoder`. This means that for a categorical variable with `n` levels, the length of the code will be `n-1`. This is necessary in order to avoid introducing feature multicolinearity, which would skew the interpretation of the results. For more information about the issue about multicolinearity in the context of linear modelling see [[1]](#References).\n",
    "<a id='src_1'></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Combine and fit:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style>#sk-container-id-1 {color: black;background-color: white;}#sk-container-id-1 pre{padding: 0;}#sk-container-id-1 div.sk-toggleable {background-color: white;}#sk-container-id-1 label.sk-toggleable__label {cursor: pointer;display: block;width: 100%;margin-bottom: 0;padding: 0.3em;box-sizing: border-box;text-align: center;}#sk-container-id-1 label.sk-toggleable__label-arrow:before {content: \"▸\";float: left;margin-right: 0.25em;color: #696969;}#sk-container-id-1 label.sk-toggleable__label-arrow:hover:before {color: black;}#sk-container-id-1 div.sk-estimator:hover label.sk-toggleable__label-arrow:before {color: black;}#sk-container-id-1 div.sk-toggleable__content {max-height: 0;max-width: 0;overflow: hidden;text-align: left;background-color: #f0f8ff;}#sk-container-id-1 div.sk-toggleable__content pre {margin: 0.2em;color: black;border-radius: 0.25em;background-color: #f0f8ff;}#sk-container-id-1 input.sk-toggleable__control:checked~div.sk-toggleable__content {max-height: 200px;max-width: 100%;overflow: auto;}#sk-container-id-1 input.sk-toggleable__control:checked~label.sk-toggleable__label-arrow:before {content: \"▾\";}#sk-container-id-1 div.sk-estimator input.sk-toggleable__control:checked~label.sk-toggleable__label {background-color: #d4ebff;}#sk-container-id-1 div.sk-label input.sk-toggleable__control:checked~label.sk-toggleable__label {background-color: #d4ebff;}#sk-container-id-1 input.sk-hidden--visually {border: 0;clip: rect(1px 1px 1px 1px);clip: rect(1px, 1px, 1px, 1px);height: 1px;margin: -1px;overflow: hidden;padding: 0;position: absolute;width: 1px;}#sk-container-id-1 div.sk-estimator {font-family: monospace;background-color: #f0f8ff;border: 1px dotted black;border-radius: 0.25em;box-sizing: border-box;margin-bottom: 0.5em;}#sk-container-id-1 div.sk-estimator:hover {background-color: #d4ebff;}#sk-container-id-1 div.sk-parallel-item::after {content: \"\";width: 100%;border-bottom: 1px solid gray;flex-grow: 1;}#sk-container-id-1 div.sk-label:hover label.sk-toggleable__label {background-color: #d4ebff;}#sk-container-id-1 div.sk-serial::before {content: \"\";position: absolute;border-left: 1px solid gray;box-sizing: border-box;top: 0;bottom: 0;left: 50%;z-index: 0;}#sk-container-id-1 div.sk-serial {display: flex;flex-direction: column;align-items: center;background-color: white;padding-right: 0.2em;padding-left: 0.2em;position: relative;}#sk-container-id-1 div.sk-item {position: relative;z-index: 1;}#sk-container-id-1 div.sk-parallel {display: flex;align-items: stretch;justify-content: center;background-color: white;position: relative;}#sk-container-id-1 div.sk-item::before, #sk-container-id-1 div.sk-parallel-item::before {content: \"\";position: absolute;border-left: 1px solid gray;box-sizing: border-box;top: 0;bottom: 0;left: 50%;z-index: -1;}#sk-container-id-1 div.sk-parallel-item {display: flex;flex-direction: column;z-index: 1;position: relative;background-color: white;}#sk-container-id-1 div.sk-parallel-item:first-child::after {align-self: flex-end;width: 50%;}#sk-container-id-1 div.sk-parallel-item:last-child::after {align-self: flex-start;width: 50%;}#sk-container-id-1 div.sk-parallel-item:only-child::after {width: 0;}#sk-container-id-1 div.sk-dashed-wrapped {border: 1px dashed gray;margin: 0 0.4em 0.5em 0.4em;box-sizing: border-box;padding-bottom: 0.4em;background-color: white;}#sk-container-id-1 div.sk-label label {font-family: monospace;font-weight: bold;display: inline-block;line-height: 1.2em;}#sk-container-id-1 div.sk-label-container {text-align: center;}#sk-container-id-1 div.sk-container {/* jupyter's `normalize.less` sets `[hidden] { display: none; }` but bootstrap.min.css set `[hidden] { display: none !important; }` so we also need the `!important` here to be able to override the default hidden behavior on the sphinx rendered scikit-learn.org. See: https://github.com/scikit-learn/scikit-learn/issues/21755 */display: inline-block !important;position: relative;}#sk-container-id-1 div.sk-text-repr-fallback {display: none;}</style><div id=\"sk-container-id-1\" class=\"sk-top-container\"><div class=\"sk-text-repr-fallback\"><pre>ColumnTransformer(transformers=[(&#x27;num&#x27;,\n",
       "                                 Pipeline(steps=[(&#x27;imputer&#x27;,\n",
       "                                                  SimpleImputer(strategy=&#x27;median&#x27;)),\n",
       "                                                 (&#x27;scaler&#x27;, StandardScaler())]),\n",
       "                                 [0, 8, 9, 10]),\n",
       "                                (&#x27;cat&#x27;,\n",
       "                                 Pipeline(steps=[(&#x27;imputer&#x27;,\n",
       "                                                  SimpleImputer(strategy=&#x27;median&#x27;)),\n",
       "                                                 (&#x27;onehot&#x27;,\n",
       "                                                  OneHotEncoder(drop=&#x27;first&#x27;))]),\n",
       "                                 [1, 2, 3, 4, 5, 6, 7, 11])])</pre><b>In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. <br />On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.</b></div><div class=\"sk-container\" hidden><div class=\"sk-item sk-dashed-wrapped\"><div class=\"sk-label-container\"><div class=\"sk-label sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-1\" type=\"checkbox\" ><label for=\"sk-estimator-id-1\" class=\"sk-toggleable__label sk-toggleable__label-arrow\">ColumnTransformer</label><div class=\"sk-toggleable__content\"><pre>ColumnTransformer(transformers=[(&#x27;num&#x27;,\n",
       "                                 Pipeline(steps=[(&#x27;imputer&#x27;,\n",
       "                                                  SimpleImputer(strategy=&#x27;median&#x27;)),\n",
       "                                                 (&#x27;scaler&#x27;, StandardScaler())]),\n",
       "                                 [0, 8, 9, 10]),\n",
       "                                (&#x27;cat&#x27;,\n",
       "                                 Pipeline(steps=[(&#x27;imputer&#x27;,\n",
       "                                                  SimpleImputer(strategy=&#x27;median&#x27;)),\n",
       "                                                 (&#x27;onehot&#x27;,\n",
       "                                                  OneHotEncoder(drop=&#x27;first&#x27;))]),\n",
       "                                 [1, 2, 3, 4, 5, 6, 7, 11])])</pre></div></div></div><div class=\"sk-parallel\"><div class=\"sk-parallel-item\"><div class=\"sk-item\"><div class=\"sk-label-container\"><div class=\"sk-label sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-2\" type=\"checkbox\" ><label for=\"sk-estimator-id-2\" class=\"sk-toggleable__label sk-toggleable__label-arrow\">num</label><div class=\"sk-toggleable__content\"><pre>[0, 8, 9, 10]</pre></div></div></div><div class=\"sk-serial\"><div class=\"sk-item\"><div class=\"sk-serial\"><div class=\"sk-item\"><div class=\"sk-estimator sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-3\" type=\"checkbox\" ><label for=\"sk-estimator-id-3\" class=\"sk-toggleable__label sk-toggleable__label-arrow\">SimpleImputer</label><div class=\"sk-toggleable__content\"><pre>SimpleImputer(strategy=&#x27;median&#x27;)</pre></div></div></div><div class=\"sk-item\"><div class=\"sk-estimator sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-4\" type=\"checkbox\" ><label for=\"sk-estimator-id-4\" class=\"sk-toggleable__label sk-toggleable__label-arrow\">StandardScaler</label><div class=\"sk-toggleable__content\"><pre>StandardScaler()</pre></div></div></div></div></div></div></div></div><div class=\"sk-parallel-item\"><div class=\"sk-item\"><div class=\"sk-label-container\"><div class=\"sk-label sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-5\" type=\"checkbox\" ><label for=\"sk-estimator-id-5\" class=\"sk-toggleable__label sk-toggleable__label-arrow\">cat</label><div class=\"sk-toggleable__content\"><pre>[1, 2, 3, 4, 5, 6, 7, 11]</pre></div></div></div><div class=\"sk-serial\"><div class=\"sk-item\"><div class=\"sk-serial\"><div class=\"sk-item\"><div class=\"sk-estimator sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-6\" type=\"checkbox\" ><label for=\"sk-estimator-id-6\" class=\"sk-toggleable__label sk-toggleable__label-arrow\">SimpleImputer</label><div class=\"sk-toggleable__content\"><pre>SimpleImputer(strategy=&#x27;median&#x27;)</pre></div></div></div><div class=\"sk-item\"><div class=\"sk-estimator sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-7\" type=\"checkbox\" ><label for=\"sk-estimator-id-7\" class=\"sk-toggleable__label sk-toggleable__label-arrow\">OneHotEncoder</label><div class=\"sk-toggleable__content\"><pre>OneHotEncoder(drop=&#x27;first&#x27;)</pre></div></div></div></div></div></div></div></div></div></div></div></div>"
      ],
      "text/plain": [
       "ColumnTransformer(transformers=[('num',\n",
       "                                 Pipeline(steps=[('imputer',\n",
       "                                                  SimpleImputer(strategy='median')),\n",
       "                                                 ('scaler', StandardScaler())]),\n",
       "                                 [0, 8, 9, 10]),\n",
       "                                ('cat',\n",
       "                                 Pipeline(steps=[('imputer',\n",
       "                                                  SimpleImputer(strategy='median')),\n",
       "                                                 ('onehot',\n",
       "                                                  OneHotEncoder(drop='first'))]),\n",
       "                                 [1, 2, 3, 4, 5, 6, 7, 11])])"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "preprocessor = ColumnTransformer(transformers=[('num', ordinal_transformer, ordinal_features),\n",
    "                                               ('cat', categorical_transformer, categorical_features)])\n",
    "preprocessor.fit(X_train)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Fit a binary logistic regression classifier to the preprocessed Adult dataset"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Preprocess the data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "X_train_proc = preprocessor.transform(X_train)\n",
    "X_test_proc = preprocessor.transform(X_test)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Training "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style>#sk-container-id-2 {color: black;background-color: white;}#sk-container-id-2 pre{padding: 0;}#sk-container-id-2 div.sk-toggleable {background-color: white;}#sk-container-id-2 label.sk-toggleable__label {cursor: pointer;display: block;width: 100%;margin-bottom: 0;padding: 0.3em;box-sizing: border-box;text-align: center;}#sk-container-id-2 label.sk-toggleable__label-arrow:before {content: \"▸\";float: left;margin-right: 0.25em;color: #696969;}#sk-container-id-2 label.sk-toggleable__label-arrow:hover:before {color: black;}#sk-container-id-2 div.sk-estimator:hover label.sk-toggleable__label-arrow:before {color: black;}#sk-container-id-2 div.sk-toggleable__content {max-height: 0;max-width: 0;overflow: hidden;text-align: left;background-color: #f0f8ff;}#sk-container-id-2 div.sk-toggleable__content pre {margin: 0.2em;color: black;border-radius: 0.25em;background-color: #f0f8ff;}#sk-container-id-2 input.sk-toggleable__control:checked~div.sk-toggleable__content {max-height: 200px;max-width: 100%;overflow: auto;}#sk-container-id-2 input.sk-toggleable__control:checked~label.sk-toggleable__label-arrow:before {content: \"▾\";}#sk-container-id-2 div.sk-estimator input.sk-toggleable__control:checked~label.sk-toggleable__label {background-color: #d4ebff;}#sk-container-id-2 div.sk-label input.sk-toggleable__control:checked~label.sk-toggleable__label {background-color: #d4ebff;}#sk-container-id-2 input.sk-hidden--visually {border: 0;clip: rect(1px 1px 1px 1px);clip: rect(1px, 1px, 1px, 1px);height: 1px;margin: -1px;overflow: hidden;padding: 0;position: absolute;width: 1px;}#sk-container-id-2 div.sk-estimator {font-family: monospace;background-color: #f0f8ff;border: 1px dotted black;border-radius: 0.25em;box-sizing: border-box;margin-bottom: 0.5em;}#sk-container-id-2 div.sk-estimator:hover {background-color: #d4ebff;}#sk-container-id-2 div.sk-parallel-item::after {content: \"\";width: 100%;border-bottom: 1px solid gray;flex-grow: 1;}#sk-container-id-2 div.sk-label:hover label.sk-toggleable__label {background-color: #d4ebff;}#sk-container-id-2 div.sk-serial::before {content: \"\";position: absolute;border-left: 1px solid gray;box-sizing: border-box;top: 0;bottom: 0;left: 50%;z-index: 0;}#sk-container-id-2 div.sk-serial {display: flex;flex-direction: column;align-items: center;background-color: white;padding-right: 0.2em;padding-left: 0.2em;position: relative;}#sk-container-id-2 div.sk-item {position: relative;z-index: 1;}#sk-container-id-2 div.sk-parallel {display: flex;align-items: stretch;justify-content: center;background-color: white;position: relative;}#sk-container-id-2 div.sk-item::before, #sk-container-id-2 div.sk-parallel-item::before {content: \"\";position: absolute;border-left: 1px solid gray;box-sizing: border-box;top: 0;bottom: 0;left: 50%;z-index: -1;}#sk-container-id-2 div.sk-parallel-item {display: flex;flex-direction: column;z-index: 1;position: relative;background-color: white;}#sk-container-id-2 div.sk-parallel-item:first-child::after {align-self: flex-end;width: 50%;}#sk-container-id-2 div.sk-parallel-item:last-child::after {align-self: flex-start;width: 50%;}#sk-container-id-2 div.sk-parallel-item:only-child::after {width: 0;}#sk-container-id-2 div.sk-dashed-wrapped {border: 1px dashed gray;margin: 0 0.4em 0.5em 0.4em;box-sizing: border-box;padding-bottom: 0.4em;background-color: white;}#sk-container-id-2 div.sk-label label {font-family: monospace;font-weight: bold;display: inline-block;line-height: 1.2em;}#sk-container-id-2 div.sk-label-container {text-align: center;}#sk-container-id-2 div.sk-container {/* jupyter's `normalize.less` sets `[hidden] { display: none; }` but bootstrap.min.css set `[hidden] { display: none !important; }` so we also need the `!important` here to be able to override the default hidden behavior on the sphinx rendered scikit-learn.org. See: https://github.com/scikit-learn/scikit-learn/issues/21755 */display: inline-block !important;position: relative;}#sk-container-id-2 div.sk-text-repr-fallback {display: none;}</style><div id=\"sk-container-id-2\" class=\"sk-top-container\"><div class=\"sk-text-repr-fallback\"><pre>LogisticRegression(max_iter=500, multi_class=&#x27;multinomial&#x27;, random_state=0)</pre><b>In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. <br />On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.</b></div><div class=\"sk-container\" hidden><div class=\"sk-item\"><div class=\"sk-estimator sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-8\" type=\"checkbox\" checked><label for=\"sk-estimator-id-8\" class=\"sk-toggleable__label sk-toggleable__label-arrow\">LogisticRegression</label><div class=\"sk-toggleable__content\"><pre>LogisticRegression(max_iter=500, multi_class=&#x27;multinomial&#x27;, random_state=0)</pre></div></div></div></div></div>"
      ],
      "text/plain": [
       "LogisticRegression(max_iter=500, multi_class='multinomial', random_state=0)"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "classifier = LogisticRegression(multi_class='multinomial',\n",
    "                                random_state=0,\n",
    "                                max_iter=500,\n",
    "                                verbose=0,\n",
    "                               )\n",
    "classifier.fit(X_train_proc, y_train)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Model assessment"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "y_pred = classifier.predict(X_test_proc)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "cm = confusion_matrix(y_test, y_pred)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "title = 'Confusion matrix for the logistic regression classifier'\n",
    "disp = ConfusionMatrixDisplay.from_estimator(classifier, \n",
    "                                             X_test_proc, \n",
    "                                             y_test,\n",
    "                                             display_labels=target_names,\n",
    "                                             cmap=plt.cm.Blues,\n",
    "                                             normalize=None,\n",
    "                                            )\n",
    "disp.ax_.set_title(title);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Test accuracy:  0.855078125\n"
     ]
    }
   ],
   "source": [
    "print('Test accuracy: ', accuracy_score(y_test, classifier.predict(X_test_proc)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Explaining the model with an explainer fitted on the preprocessed data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To speed up computation, we will use a background dataset with only `100` samples."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "start_example_idx = 0\n",
    "stop_example_idx = 100\n",
    "background_data = slice(start_example_idx, stop_example_idx)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "First, we group the categorical variables."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "def make_groups(num_feats_names, cat_feats_names, feat_enc_dim):\n",
    "    \"\"\"\n",
    "    Given a list with numerical feat. names, categorical feat. names\n",
    "    and a list specifying the lengths of the encoding for each cat.\n",
    "    varible, the function outputs a list of group names, and a list\n",
    "    of the same len where each entry represents the column indices that\n",
    "    the corresponding categorical feature \n",
    "    \"\"\"\n",
    "    \n",
    "    group_names = num_feats_names + cat_feats_names\n",
    "    groups = []\n",
    "    cat_var_idx = 0\n",
    "    \n",
    "    for name in group_names: \n",
    "        if name in num_feats_names:\n",
    "            groups.append(list(range(len(groups), len(groups) + 1)))\n",
    "        else:\n",
    "            start_idx = groups[-1][-1] + 1 if groups else 0\n",
    "            groups.append(list(range(start_idx, start_idx + feat_enc_dim[cat_var_idx] )))\n",
    "            cat_var_idx += 1\n",
    "    \n",
    "    return group_names, groups\n",
    "            \n",
    "def sparse2ndarray(mat, examples=None):\n",
    "    \"\"\"\n",
    "    Converts a scipy.sparse.csr.csr_matrix to a numpy.ndarray.\n",
    "    If specified, examples is slice object specifying which selects a\n",
    "    number of rows from mat and converts only the respective slice.\n",
    "    \"\"\"\n",
    "    \n",
    "    if examples:\n",
    "        return mat[examples, :].toarray()\n",
    "    \n",
    "    return mat.toarray()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "# obtain the indices of the categorical and the numerical features from the pipeline.\n",
    "numerical_feats_idx  = preprocessor.transformers_[0][2]\n",
    "categorical_feats_idx  = preprocessor.transformers_[1][2]\n",
    "num_feats_names = [feature_names[i] for i in numerical_feats_idx]\n",
    "cat_feats_names = [feature_names[i] for i in categorical_feats_idx]\n",
    "perm_feat_names = num_feats_names + cat_feats_names \n",
    "ohe = preprocessor.transformers_[1][1].named_steps['onehot']\n",
    "feat_enc_dim = [len(cat_enc) - 1 for cat_enc in ohe.categories_]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "# create the groups\n",
    "X_train_proc_d = sparse2ndarray(X_train_proc, examples=background_data)\n",
    "group_names, groups = make_groups(num_feats_names, cat_feats_names, feat_enc_dim)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Having created the groups, we are now ready to instantiate the explainer and explain our set."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "KernelShap(meta={'name': 'KernelShap', 'type': 'blackbox', 'explanations': ['local', 'global'], 'params': {'groups': [[0], [1], [2], [3], [4, 5, 6, 7, 8, 9, 10, 11], [12, 13, 14, 15, 16, 17], [18, 19, 20], [21, 22, 23, 24, 25, 26, 27, 28], [29, 30, 31, 32, 33], [34, 35, 36, 37], [38], [39, 40, 41, 42, 43, 44, 45, 46, 47, 48]], 'group_names': ['Age', 'Capital Gain', 'Capital Loss', 'Hours per week', 'Workclass', 'Education', 'Marital Status', 'Occupation', 'Relationship', 'Race', 'Sex', 'Country'], 'weights': None, 'summarise_background': False}})"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pred_fcn = classifier.predict_proba\n",
    "grp_lr_explainer = KernelShap(pred_fcn, link='logit', feature_names=perm_feat_names, seed=0)\n",
    "grp_lr_explainer.fit(X_train_proc_d, group_names=group_names, groups=groups)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We select only a small fraction of the testing set to explain for the purposes of this example."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "def split_set(X, y, fraction, random_state=0):\n",
    "    \"\"\"\n",
    "    Given a set X, associated labels y, split\\\\s a fraction y from X.\n",
    "    \"\"\"\n",
    "    _, X_split, _, y_split = train_test_split(X, \n",
    "                                              y, \n",
    "                                              test_size=fraction, \n",
    "                                              random_state=random_state,\n",
    "                                             )\n",
    "    print(\"Number of records: {}\".format(X_split.shape[0]))\n",
    "    print(\"Number of class {}: {}\".format(0, len(y_split) - y_split.sum()))\n",
    "    print(\"Number of class {}: {}\".format(1, y_split.sum()))\n",
    "    \n",
    "    return X_split, y_split"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Number of records: 26\n",
      "Number of class 0: 20\n",
      "Number of class 1: 6\n"
     ]
    }
   ],
   "source": [
    "fraction_explained = 0.01 \n",
    "X_explain, y_explain = split_set(X_test, \n",
    "                                 y_test, \n",
    "                                 fraction_explained, \n",
    "                                 )\n",
    "X_explain_proc = preprocessor.transform(X_explain)\n",
    "X_explain_proc_d = sparse2ndarray(X_explain_proc)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "grouped_explanation = grp_lr_explainer.explain(X_explain_proc_d)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Explaining with an explainer fitted on the raw data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To explain with an explainer fitted on the raw data, we make the preprocessor part of the predictor, as shown below."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "pred_fcn = lambda x: classifier.predict_proba(preprocessor.transform(x))\n",
    "lr_explainer = KernelShap(pred_fcn, link='logit', feature_names=feature_names, seed=0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We use the same background dataset to fit the explainer."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "KernelShap(meta={'name': 'KernelShap', 'type': 'blackbox', 'explanations': ['local', 'global'], 'params': {'groups': None, 'group_names': None, 'weights': None, 'summarise_background': False}})"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "lr_explainer.fit(X_train[background_data])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We explain the same dataset as before."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "explanation = lr_explainer.explain(X_explain)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Results comparison"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To show that fitting the explainer on the raw data and combining the preprocessor with the classifier gives the same results as grouping the variables and fitting the explainer on the preprocessed data, we check to see that the same features are considered as most important when combining the two approaches."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_ranked_values(explanation):\n",
    "    \"\"\"\n",
    "    Retrives a tuple of (feature_effects, feature_names) for\n",
    "    each class explained. A feature's effect is its average\n",
    "    shap value magnitude across an array of instances.\n",
    "    \"\"\"\n",
    "    \n",
    "    ranked_shap_vals = []\n",
    "    for cls_idx in range(len(explanation.shap_values)):\n",
    "        this_ranking = (\n",
    "            explanation.raw['importances'][str(cls_idx)]['ranked_effect'],\n",
    "            explanation.raw['importances'][str(cls_idx)]['names']\n",
    "                       )\n",
    "        ranked_shap_vals.append(this_ranking)\n",
    "    \n",
    "    return ranked_shap_vals\n",
    "\n",
    "def compare_ranking(ranking_1, ranking_2, methods=None):\n",
    "    for i, (combined, grouped) in enumerate(zip(ranking_1, ranking_2)):\n",
    "        print(f\"Class: {i}\")\n",
    "        c_names, g_names = combined[1], grouped[1]\n",
    "        c_mag, g_mag = combined[0], grouped[0]\n",
    "        different = []\n",
    "        for i, (c_n, g_n) in enumerate(zip(c_names, g_names)):\n",
    "            if c_n != g_n:\n",
    "                different.append((i, c_n, g_n))\n",
    "        if different:\n",
    "            method_1 = methods[0] if methods else \"Method_1\"\n",
    "            method_2 = methods[1] if methods else \"Method_2\"\n",
    "            i, c_ns, g_ns = list(zip(*different))\n",
    "            data = {\"Rank\": i, method_1: c_ns, method_2: g_ns}\n",
    "            df = pd.DataFrame(data=data)\n",
    "            print(\"Found the following rank differences:\")\n",
    "            print(df)\n",
    "        else:\n",
    "            print(\"The methods provided the same ranking for the feature effects.\")\n",
    "            print(f\"The ranking is: {c_names}\")\n",
    "        print(\"\")\n",
    "        \n",
    "def reorder_feats(vals_and_names, src_vals_and_names):\n",
    "    \"\"\"Given a two tuples, each containing a list of ranked feature\n",
    "    shap values and the corresponding feature names, the function \n",
    "    reorders the values in vals according to the order specified in\n",
    "    the list of names contained in src_vals_and_names.\n",
    "    \"\"\"\n",
    "    \n",
    "    _, src_names = src_vals_and_names\n",
    "    vals, names = vals_and_names\n",
    "    reordered = np.zeros_like(vals)\n",
    "    \n",
    "    for i, name in enumerate(src_names):\n",
    "        alt_idx = names.index(name)\n",
    "        reordered[i] = vals[alt_idx]\n",
    "    \n",
    "    return reordered, src_names\n",
    "\n",
    "def compare_avg_mag_shap(class_idx, comparisons, baseline, **kwargs):\n",
    "    \"\"\"\n",
    "    Given a list of tuples, baseline, containing the feature values and a list with feature names \n",
    "    for each class and, comparisons, a list of lists with tuples with the same structure , the \n",
    "    function reorders the values of the features in comparisons entries according to the order \n",
    "    of the feature names provided in the baseline entries and displays the feature values for comparison.\n",
    "    \"\"\"\n",
    "    \n",
    "    methods = kwargs.get(\"methods\", [f\"method_{i}\" for i in range(len(comparisons) + 1)])\n",
    "    \n",
    "    n_features = len(baseline[class_idx][0])\n",
    "    \n",
    "    # bar settings\n",
    "    bar_width = kwargs.get(\"bar_width\", 0.05)\n",
    "    bar_space = kwargs.get(\"bar_space\", 2)\n",
    "    \n",
    "    # x axis \n",
    "    x_low = kwargs.get(\"x_low\", 0.0)\n",
    "    x_high = kwargs.get(\"x_high\", 1.0)\n",
    "    x_step = kwargs.get(\"x_step\", 0.05)\n",
    "    x_ticks = np.round(np.arange(x_low, x_high + x_step, x_step), 3)\n",
    "\n",
    "    # y axis (these are the y coordinate of start and end of each group \n",
    "    # of bars)\n",
    "    start_y_pos = np.array(np.arange(0, n_features))*bar_space\n",
    "    end_y_pos = start_y_pos + bar_width*len(methods)\n",
    "    y_ticks = 0.5*(start_y_pos + end_y_pos)\n",
    "    \n",
    "    # figure \n",
    "    fig_x = kwargs.get(\"fig_x\", 10)\n",
    "    fig_y = kwargs.get(\"fig_y\", 7)\n",
    "    \n",
    "    # fontsizes \n",
    "    title_font = kwargs.get(\"title_fontsize\", 20)\n",
    "    legend_font = kwargs.get(\"legend_fontsize\", 20)\n",
    "    tick_labels_font = kwargs.get(\"tick_labels_fontsize\", 20)\n",
    "    axes_label_fontsize = kwargs.get(\"axes_label_fontsize\", 10)\n",
    "    \n",
    "    # labels \n",
    "    title = kwargs.get(\"title\", None)\n",
    "    ylabel = kwargs.get(\"ylabel\", None)\n",
    "    xlabel = kwargs.get(\"xlabel\", None)\n",
    "    \n",
    "    # process input data \n",
    "    methods = list(reversed(methods))\n",
    "    base_vals = baseline[class_idx][0] \n",
    "    ordering = baseline[class_idx][1]\n",
    "    comp_vals = []\n",
    "    \n",
    "    # reorder the features so that they match the order of the baseline (ordering)\n",
    "    for comparison in comparisons:\n",
    "        vals, ord_ = reorder_feats(comparison[class_idx], baseline[class_idx])\n",
    "        comp_vals.append(vals)\n",
    "        assert ord_ is ordering \n",
    "        \n",
    "    all_vals = [base_vals] + comp_vals\n",
    "    data = dict(zip(methods, all_vals))\n",
    "    df = pd.DataFrame(data=data, index=ordering)\n",
    "    \n",
    "    # plotting logic\n",
    "    fig, ax = plt.subplots(figsize=(fig_x, fig_y))\n",
    "\n",
    "    for i, col in enumerate(df.columns):\n",
    "        values = list(df[col])\n",
    "        y_pos = [y + bar_width*i for y  in start_y_pos] \n",
    "        ax.barh(y_pos, list(values), bar_width, label=col)\n",
    "    \n",
    "    # add ticks, legend and labels\n",
    "    ax.set_xticks(x_ticks)\n",
    "    ax.set_xticklabels([str(x) for x in x_ticks], rotation=45, fontsize=tick_labels_font)\n",
    "    ax.set_xlabel(xlabel, fontsize=axes_label_fontsize)\n",
    "    ax.set_yticks(y_ticks)\n",
    "    ax.set_yticklabels(ordering, fontsize=tick_labels_font)\n",
    "    ax.set_ylabel(ylabel, fontsize=axes_label_fontsize)\n",
    "    ax.invert_yaxis()  # labels read top-to-bottom\n",
    "    ax.legend(fontsize=legend_font)   \n",
    "\n",
    "    plt.grid(True)\n",
    "    plt.title(title, fontsize=title_font)\n",
    "\n",
    "    return ax, fig, df"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Class: 0\n",
      "The methods provided the same ranking for the feature effects.\n",
      "The ranking is: ['Marital Status', 'Capital Gain', 'Education', 'Occupation', 'Sex', 'Relationship', 'Age', 'Hours per week', 'Workclass', 'Capital Loss', 'Country', 'Race']\n",
      "\n",
      "Class: 1\n",
      "The methods provided the same ranking for the feature effects.\n",
      "The ranking is: ['Marital Status', 'Capital Gain', 'Education', 'Occupation', 'Sex', 'Relationship', 'Age', 'Hours per week', 'Workclass', 'Capital Loss', 'Country', 'Race']\n",
      "\n"
     ]
    }
   ],
   "source": [
    "ranked_grouped_shap_vals = get_ranked_values(grouped_explanation)\n",
    "ranked_shal_vals_raw = get_ranked_values(explanation)\n",
    "compare_ranking(ranked_grouped_shap_vals, ranked_shal_vals_raw)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Above we can see that both methods returned the same feature importances. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 720x504 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "class_idx = 0 \n",
    "\n",
    "ax, fig, _ = compare_avg_mag_shap(class_idx, \n",
    "                               [ranked_shal_vals_raw], \n",
    "                               ranked_grouped_shap_vals, \n",
    "                               methods=('raw_data', 'grouped'),\n",
    "                               bar_width=0.5,\n",
    "                               tick_labels_fontsize=12,\n",
    "                               legend_fontsize=12,\n",
    "                               title_fontsize=15,\n",
    "                               xlabel=\"Features  effects (class {})\".format(0),\n",
    "                               ylabel=\"Feature\",\n",
    "                               axes_label_fontsize=15,\n",
    "                               )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can see that the shap values are very similar. The differences appear because the regression dataset generated in order to compute the shap values differes slightly between the two runs due to the difference in the order of the features in the background dataset.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### References"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<a id='References'></a>\n",
    "\n",
    "[[1]](#src_1) *Mahto, K.K., 2019. \"One-Hot-Encoding, Multicollinearity and the Dummy Variable Trap\". Retrieved 02 Feb 2020*  [(link)](https://towardsdatascience.com/one-hot-encoding-multicollinearity-and-the-dummy-variable-trap-b5840be3c41a)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
